'use strict';

const Command = require('@test-hy-in/command');
const path = require('path');
const userHome = require('user-home');
const glob = require('glob');
const ejs = require('ejs');
const Package = require('@test-hy-in/package');
const {spinnerStart,sleep,execAsync} = require('@test-hy-in/utils');
const log = require('@test-hy-in/log');
const inquirer = require('inquirer');
const fse = require('fs-extra');
const semver = require('semver');
const getProjectTemplate = require('./getProjectTemplate');


const TYPE_PROJECT = 'project';
const TYPE_COMPONENT = 'component';
const TEMPLATE_TYPE_NORMAL = 'normal';
const TEMPLATE_TYPE_CUSTOM = 'custom';
const WHITE_COMMAND = ['npm','cnpm'];


class InitCommand extends Command {
    // 参数的初始化
    init(){
        this.projectName = this._argv[0]||'';
        this.force = !!this._cmd.force;
        log.verbose('projectName',this.projectName)
        log.verbose('force',this.force)
    }
    async exec(){
        // console.log('init 业务逻辑')
        try{
            // 1、准备阶段
            const projectInfo = await this.prepare();
            if(projectInfo){
                // 2、下载模板
                this.projectInfo = projectInfo;
                await this.downloadTemplate()
                // 3、安装模板
                await this.installTemplate();
            }else{}
        }catch(e){
            log.error(e.message)
        }
    }

    async installTemplate(){
        if(this.templateInfo){
            if(!this.templateInfo.type){
                this.templateInfo.type = TEMPLATE_TYPE_NORMAL
            };
            if(this.templateInfo.type === TEMPLATE_TYPE_NORMAL){
                // 标准安装
                await this.installNormalTempalte()
            }else if(this.templateInfo.type === TEMPLATE_TYPE_CUSTOM){
                // 自定义安装
                await this.installCustomTempalte()
            }else{
            throw new Error('无法识别项目模板！')
            }
        }else{
            throw new Error('项目模板不存在！')
        }
    }

    async installNormalTempalte(){
        let spinner = spinnerStart('正在安装标准模板...');
        await sleep();
        try{
            const templatePath = path.resolve(this.templateNpm.cacheFilePath,'template');
            console.log('templatePath:',templatePath)
            const targetPath = process.cwd();
            fse.ensureDirSync(templatePath);
            fse.ensureDirSync(targetPath);
            fse.copySync(templatePath,targetPath)
        }catch(e){
            throw e
        }finally{
            spinner.stop(true)
            log.success('模板安装成功!')
        };
        console.log(this.templateInfo)
        // 修改package.json中name
        // const templateIgnore = this.templateInfo.ignore || [];
        // const ignore = ['**/node_modules/**',...templateIgnore];
        const ignore = ['node_modules/**','public/**'];
        await this.ejsRender({ignore});
        const {installCommand,startCommand} = this.templateInfo;
        // 依赖安装
        await this.execCommand(installCommand,'安装依赖过程中失败！')
        // 启动命令执行
        await this.execCommand(startCommand,'启动命令执行失败！')
    }

    async installCustomTempalte(){
        console.log('安装自定义模板')
    }
    // ejs模板渲染修改package.json
    async ejsRender(options){
        const dir = process.cwd();
        const projectInfo = this.projectInfo;
        console.log("projectInfo:",projectInfo)
        return new Promise((resolve,reject)=>{
            glob('**',{
                cwd:dir,
                ignore:options.ignore || '',
                nodir:true
            },function(err,files){
                if(err){
                    reject(err)
                };
                Promise.all(files.map(file=>{
                    const filePath = path.join(dir,file);
                    return new Promise((resolve1,reject1)=>{
                        ejs.renderFile(filePath,projectInfo,{},(err,result)=>{
                            if(err){
                                reject1(err)
                            }else{
                                // console.log('filePath:',filePath)
                                // console.log('result:',result)
                                fse.writeFileSync(filePath,result);
                                resolve1(result)
                            }
                        })
                    })
                })).then(()=>{
                    resolve()
                }).catch(err=>{
                    reject(err)
                })
            })
        })
    }

    // 判断命令是否在白名单
    checkCommand(cmd){
        if(WHITE_COMMAND.includes(cmd)){
            return cmd
        }
        return null
    }

    // 执行命令方法封装
    async execCommand(command,errMsg){
        let ret;
        if(command){
            const cmdArray = command.split(' ');
            const cmd = this.checkCommand(cmdArray[0]);
            if(!cmd){
                throw new Error('命令不存在！命令：'+command)
            };
            const args = cmdArray.slice(1);
            ret = await execAsync(cmd,args,{
                stdio:'inherit',
                cwd:process.cwd()
            });
        };
        if(ret !== 0){
            throw new Error(errMsg)
        }
        return ret
    }


    async prepare(){
        const template = await getProjectTemplate();
        if(!template || template.length === 0){
            throw new Error('项目模板不存在')
        };
        this.template = template;
        // 当前文件目录（主目录，不是当前文件的执行目录）
        const localPath = process.cwd();
        // 1、判断当前目录是否为空
        if(!this.isDirEmpty(localPath)){
            let ifContinue = false;
            if(!this.force){
                // 询问是否继续创建
                ifContinue = (await inquirer.prompt({
                    type:'confirm',
                    name:'ifContinue',
                    default:false,
                    message:'当前文件夹不为空，是否继续创建项目？'
                })).ifContinue;
                log.verbose('ifContinue:',ifContinue)
                if(!ifContinue){
                    return
                };
            };
            // 2、是否启动强制更新
            if(ifContinue || this.force){
                // 给用户二次确认
                log.verbose('给用户二次确认,是否启动强制更新')
                const {confirmDelete} = await inquirer.prompt({
                    type:'confirm',
                    name:'confirmDelete',
                    default:false,
                    message:'是否确认清空当前目录下的文件？'
                })
                log.verbose('confirmDelete:',confirmDelete)
                if(confirmDelete){
                    // 清空当前目录
                    fse.emptyDirSync(localPath)
                }
            }
        }
        return this.getProjectInfo()
    }
    
    async getProjectInfo(){
        let projectInfo = {};
        // 判断init初始化时候，传入项目名称没 this.projectName
        let isProjectName = this.projectName?true:false;
        // 如果输入项目名称，则更新项目信息中的项目名称
        if(isProjectName){
            projectInfo.projectName = this.projectName
        };
        // 3、选择创建项目或组件(创建的类型)
        const {type} = await inquirer.prompt({
            type:'list',
            name:'type',
            message:'请选择初始化类型',
            default:TYPE_PROJECT,
            choices:[{
                name:'项目',
                value:TYPE_PROJECT
            },{
                name:'组件',
                value:TYPE_COMPONENT
            }]
        });
        if(type === TYPE_PROJECT){
            // 4、填写创建项目的基本信息
            const projectPrompt = [];
            // 项目名称
            const projectNamePrompt = {
                type:'input',
                name:'projectName',
                message:'请输入项目名称',
                default:'',
                validate:function(v){
                    return typeof v === 'string'
                },
                filter:function(v){
                    return v
                }
            };
            if(!isProjectName){
                // init初始化时候，没有输入项目名称
                projectPrompt.push(projectNamePrompt)
            };
            projectPrompt.push(
            {
                type:'input',
                name:'projectVersion',
                message:'请输入项目版本号',
                default:'0.0.1',
                validate:function(v){
                    const done = this.async();
                    setTimeout(function(){
                        if(!(!!semver.valid(v))){
                            done('请输入合法的版本号')
                            return
                        }
                        done(null,true)
                    },0)
                    return !!semver.valid(v)
                },
                filter:function(v){
                    if(!!semver.valid(v)){
                        return semver.valid(v)
                    }else{
                        return v
                    }
                }
            },{
                type:'list',
                name:'projectTemplate',
                message:'请选择项目模板',
                choices:this.createTemplateChoice()
            })
            const project = await inquirer.prompt(projectPrompt);
            projectInfo = {
                ...projectInfo,
                type,
                ...project
            }
        }else if(type === TYPE_COMPONENT){};
        // 生成className
        if(projectInfo.projectName){
            projectInfo.name = projectInfo.projectName;
            projectInfo.className = projectInfo.projectName;
        };
        if(projectInfo.projectVersion){
            projectInfo.version = projectInfo.projectVersion;
        };
        // return 项目基本信息(object)
        return projectInfo
    }


    // 下载模板
    async downloadTemplate(){
        const {projectTemplate} = this.projectInfo;
        const templateInfo = this.template.find(tem =>
            tem.npmName === projectTemplate
        );
        const targetPath = path.resolve(userHome,'.test-hy-in','template');
        const storeDir = path.resolve(userHome,'.test-hy-in','template','node_modules');
        const {npmName,version} = templateInfo;
        this.templateInfo = templateInfo;
        // log.verbose("targetPath:",targetPath)
        // log.verbose("storeDir:",storeDir)
        // log.verbose("templateInfo:",templateInfo)
        const templateNpm = new Package({
            targetPath,
            storeDir,
            packageName:npmName,
            packageVersion:version
        });
        if(!await templateNpm.exists()){
            const spinner = spinnerStart('正在下载模板...')
            await sleep();
            try{
                await templateNpm.install()
            }catch(e){
                throw e
            }finally{
                spinner.stop(true);
                if(await templateNpm.exists()){
                    log.success('下载模板成功')
                    this.templateNpm = templateNpm
                }
            }
        }else{
            const spinner = spinnerStart('正在更新模板...')
            await sleep()
            try{
                await templateNpm.updata()
            }catch(e){
                throw e
            }finally{
                spinner.stop(true)
                if(await templateNpm.exists()){
                    log.success('更新模板成功')
                    this.templateNpm = templateNpm
                }
            }
        };
        // 1、通过项目api获取项目模板信息
        // 1.1、通过egg.js搭建一套后端系统
        // 1.2、通过npm存储项目模板（vue-cli、vue-element-admin）
        // 1.3、将项目模板信息存储到mongodb数据库中
        // 1.4、通过egg.js获取mongodb中的数据且通过api返回

    }
    // 判断当前路径是否为空目录
    isDirEmpty(localPath){
        // 判断是否存在文件
        let fileList = fs.readdirSync(localPath);
        fileList = fileList.filter(file=>(
            !file.startsWith('.') && ['node_modules'].indexOf(file) < 0
        ));
        return !fileList || fileList.length <= 0;
    }
    // 根据mongodb中模板数据返回模板信息
    createTemplateChoice(){
        return this.template.map(item => ({
            name:item.name,
            value:item.npmName
        }))
    }
    
}

function init(argv) {
    return new InitCommand(argv)
}

module.exports = init;
module.exports.InitCommand = InitCommand;